#include "clWrapper.hh"
#include "deviceWrapper.hh"
#include <fstream>


int main(){

	// Getting information about the Platforms
	clWrapper platofrmCL;
	std::cout << "Available Platforms" << std::endl;
	std::cout << platofrmCL;

	// Get the Platform from the USER
	cl::Platform pl = platofrmCL.getPlatform();	
	std::cout << "Using the Following Platform: " << std::endl;
	clWrapper platform_sel(pl);
	std::cout << platform_sel;


	// Obtain information about the Devices present in the Selected Platform
	std::cout << "Available Devices" << std::endl;
	deviceWrapper deviceCL(pl);
	std::cout << deviceCL;

	cl::Device device;	cl::Context context; cl::CommandQueue cmdq; cl::Program::Sources sources;
	cl::Program program; 

	// Set the Device, Context, Program, Commandqueue, sources. Can be modified later to make number of Programs instead of one program.
	deviceCL.getDeviceProgram("hellocl.cl", device, context, cmdq, sources, program);
	std::cout << "Using the Following Device: " << std::endl;
	deviceWrapper deviceSel(device);
	std::cout << deviceSel;


	/*************************************************************************************************************8
	*/
	// Task 1:
	// Assigning Memory Buffers

	std::cout << "Task 1: Reading String and  Hello OpenCL! and Overwriting ???????????????\n" << std::endl;
	// Input Buffer
	std::string in("Hello OpenCL!");
	unsigned int N = in.length();
	const char * in1 = in.c_str();
	cl::Buffer inCL(context, CL_MEM_READ_ONLY, in.length()+1);
	// Output Buffer
	const std::string hw("???????????????");
	char * outH = new char[hw.length()+1];
	strcpy(outH, hw.c_str());
	cl::Buffer outCL(context, CL_MEM_READ_WRITE, in.length());

	cl::Kernel kernelCL;
	try{
		kernelCL =cl::Kernel(program, "hellocl");
		cmdq.enqueueWriteBuffer(inCL, CL_TRUE, 0, in.length()+1, in1);
		kernelCL.setArg(0, inCL);
		kernelCL.setArg(1, outCL);
		kernelCL.setArg(2, N);

		cmdq.enqueueNDRangeKernel(kernelCL, cl::NullRange, cl::NDRange(in.length()), cl::NDRange(1,1));	
		cmdq.enqueueReadBuffer(outCL, CL_TRUE, 0, in.length(), outH);
		std::cout << outH << std::endl;
		std::cout << "\n---------Finishing Task 1-----------" << std::endl;

		/*************************************************************************************************************8

		// Task 2:
		// Assigning Memory Buffers */

		std::cout << "Task 2: ( Using CL_MEM_USE_HOST_PTR)------- \n" << std::endl;
		// Input Buffer
		std::string host("Bye OpenCL!!!");
		unsigned int N2 = host.length();
		char * host1 = new char[host.length()+1];
		strcpy(host1, host.c_str());
		cl::Buffer hostCL(context, CL_MEM_USE_HOST_PTR, host.length()+1, host1);		
		cmdq.enqueueCopyBuffer(inCL, hostCL, 0, 0,in.length()+1);
		char * mappedPTr= static_cast<char*>(cmdq.enqueueMapBuffer(hostCL, true, CL_MAP_READ, 0, host.length()));
		//std::cout << mappedPTr << std::endl;

		std::string star = "***************";
		star.replace(0,N2,mappedPTr);
		std::cout << star<< std::endl;
		cmdq.enqueueUnmapMemObject(hostCL,(void *)mappedPTr);
		std::cout << "\n--------Finishing Task 2--------------" << std::endl;


		//	cmdq.enqueueCopyBuffer(
		/*************************************************************************************************************8
		*/
		// Task 3
		//	system("pause");
		std::cout << "Task 3: (Vector Addition)--------------\n" << std::endl;

		deviceCL.getDeviceProgram("vector_add_cpu.cl", device, context, cmdq, sources, program);
		//initialize our CPU memory arrays, send them to the device and set the kernel arguements
		const int num = 13;
		float *a = new float[num];
		float *b = new float[num];
		float *c= new float[num];
		for(int i=0; i < num; ++i)
		{
			a[i] = 1.0f;
			b[i] = 1.0f;
		}
		cl::Buffer cl_a(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, sizeof(float) * num, &a[0]);
		cl::Buffer cl_b(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, sizeof(float) * num, &b[0]);
		cl::Buffer cl_c(context, CL_MEM_WRITE_ONLY, sizeof(float) * num);
		cl::Kernel kernelVec;	
		kernelVec =cl::Kernel(program, "vector_add_cpu");
		kernelVec.setArg(0, cl_a);
		kernelVec.setArg(1, cl_b);
		kernelVec.setArg(2, cl_c);
		kernelVec.setArg(3, num);

		cmdq.enqueueNDRangeKernel(kernelVec, cl::NullRange, cl::NDRange(128), cl::NDRange(1));	
		cmdq.enqueueReadBuffer(cl_c, CL_TRUE, 0, sizeof(float) * num, c);

		for(int i=0; i < num; ++i)
			std::cout << c[i]<< std::endl;	
		cmdq.finish();
		std::cout << "\n----------Finishing Task 3----------------" << std::endl;

	}
	catch (cl::Error er) {
		printf("ERROR: %s(%d)\n", er.what(), er.err());
	}
	/*************************************************************************************************************
	*/	
	return 0;
}
